////
/// @group core.transition
////

@use "sass:color";
@use "sass:map";
@use "../utils";
@use "../border-radius";
@use "../theme/colors";
@use "../theme/theme";

/// Set to `true` to disable all transition styles.
/// @type Boolean
$disable-transitions: false !default;

/// Set to `true` to disable the collapse transition styles.
/// @type Boolean
$disable-collapse-transition: $disable-transitions !default;

/// Set to `true` to disable the cross-fade transition styles.
/// @type Boolean
$disable-cross-fade-transition: $disable-transitions !default;

/// Set to `true` to disable the scale transition styles.
/// @type Boolean
$disable-scale-transition: $disable-transitions !default;

/// Set to `true` to disable the scale-y transition styles.
/// @type Boolean
$disable-scale-y-transition: $disable-scale-transition !default;

/// Set to `true` to disable the skeleton placeholder transition styles.
/// @type Boolean
$disable-skeleton-placeholder: $disable-transitions !default;

/// Set to `true` to disable the slide transition styles.
/// @type Boolean
$disable-slide-transition: $disable-transitions !default;

/// Set to `true` to disable the max-width transition styles.
/// @type Boolean
$disable-max-width-transition: $disable-transitions !default;

/// Set to `true` to disable the max-width transition gap styles.
/// @type Boolean
$disable-max-width-transition-gap: $disable-max-width-transition !default;

/// The default transition duration for linear transitions. i.e. a transition
/// that has the same enter and leave duration.
/// @type Number
$linear-duration: 0.15s !default;

/// The default transition duration when an element enters less quickly than it
/// leaves.
/// @type Number
$enter-duration: 0.2s !default;

/// The default transition duration when an element leaves more quickly than it
/// enters.
/// @type Number
$leave-duration: 0.15s !default;

/// A default transition-timing-function to use when an element should appear quickly.
/// @type String
$sharp-timing-function: cubic-bezier(0.4, 0, 0.6, 1) !default;

/// A default transition-timing-function to use when an element should appear
/// and exit at the same speed.
/// @type String
$linear-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !default;

/// A default transition-timing-function to use when an element should
/// accelerate during the transition.
/// @type String
$acceleration-timing-function: cubic-bezier(0.4, 0, 1, 1) !default;

/// A default transition-timing-function to use when an element should
/// decelerate during the transition.
/// @type String
$deceleration-timing-function: cubic-bezier(0, 0, 0.2, 1) !default;

/// The starting position for the `CrossFade` transition.
/// @type Number
$cross-fade-translate-distance: -1rem !default;

/// The cross fade transition duration.
/// @type Number
$cross-fade-transition-duration: 0.3s !default;

/// The scale transition enter duration.
/// @type Number
$scale-transition-enter-duration: $enter-duration !default;

/// The scale transition leave duration.
/// @type Number
$scale-transition-leave-duration: $leave-duration !default;

/// The scale transition y (vertical) enter duration.
/// @type Number
$scale-y-transition-enter-duration: $enter-duration !default;

/// The scale transition y (vertical) leave duration.
/// @type Number
$scale-y-transition-leave-duration: $leave-duration !default;

/// The default skeleton placeholder height.
/// @type Number
$skeleton-placeholder-height: 1.125em !default;

/// The default skeleton placeholder width`.
/// @type Number
$skeleton-placeholder-width: 43% !default;

/// The default skeleton placeholder border-radius.
/// @type Number
$skeleton-placeholder-border-radius: border-radius.get-var(xs) !default;

/// The default light theme skeleton placeholder background color.
/// @type Color
$skeleton-placeholder-light-background-color: #000 !default;

/// The default dark theme skeleton placeholder background color.
/// @type Color
$skeleton-placeholder-dark-background-color: colors.$grey-300 !default;

/// The default skeleton placeholder background color
/// @type Color
$skeleton-placeholder-background-color: theme.get-default-color(
  $skeleton-placeholder-dark-background-color,
  $skeleton-placeholder-light-background-color
) !default;

/// The default skeleton placeholder `@keyframes` styles.
/// @type Map
$skeleton-placeholder-animation: (
  0%: (
    opacity: 0.06,
  ),
  60%: (
    opacity: 0.1,
  ),
  80%: (
    opacity: 0.08,
  ),
  100%: (
    opacity: 0.06,
  ),
) !default;

/// The default skeleton placeholder animation duration.
/// @type Number
$skeleton-placeholder-animation-duration: 1.8s !default;

/// The default skeleton placeholder animation timing function.
/// @type String
$skeleton-placeholder-animation-timing-function: ease-in-out !default;

/// The default slide transition duration.
/// @type Number
$slide-duration: $linear-duration !default;

/// The default slide transition timing function.
/// @type String
$slide-timing-function: $linear-timing-function !default;

/// The default max width transition duration.
/// @type Number
$max-width-transition-duration: $linear-duration !default;

/// The default max width transition timing function.
/// @type Number
$max-width-transition-timing-function: $linear-timing-function !default;

/// The default max width transition's `max-width`
/// @type Number
$max-width-transition-default: 100% !default;

/// The available configurable css variables and mostly used internally for the
/// `get-var`, `set-var`, and `use-var` utils.
/// @type List
$transition-variables: (
  skeleton-placeholder-background-color,
  skeleton-placeholder-height,
  skeleton-placeholder-width,
  slide-duration,
  max-width,
  max-width-gap
);

/// @param {String} name - The supported variable name
/// @param {any} fallback [null] - An optional fallback value
/// @returns {String} a `var()` statement
@function transition-get-var($name, $fallback: null) {
  $var: utils.get-var-name($transition-variables, $name, "transition");
  @if $fallback {
    @return var(#{$var}, #{$fallback});
  }

  @return var(#{$var});
}

/// @param {String} name - The supported variable name
/// @param {any} value - The value to set the variable to. Supports `null` which
/// will just be a no-op.
@mixin transition-set-var($name, $value) {
  @if $value {
    #{utils.get-var-name($transition-variables, $name, "transition")}: #{$value};
  }
}

/// @param {String} property - The css property to apply the variable to
/// @param {String} name [$property] - The supported variable name
/// @param {any} fallback [null] - An optional fallback value if the variable
/// has not been set
@mixin transition-use-var($property, $name: $property, $fallback: null) {
  #{$property}: transition-get-var($name, $fallback);
}

/// Applies the light the variables based on feature flags
@mixin transition-use-light-theme {
  @if not $disable-skeleton-placeholder {
    @include transition-set-var(
      skeleton-placeholder-background-color,
      $skeleton-placeholder-light-background-color
    );
  }
}

/// Applies the dark the variables based on feature flags
@mixin transition-use-dark-theme {
  @if not $disable-skeleton-placeholder {
    @include transition-set-var(
      skeleton-placeholder-background-color,
      $skeleton-placeholder-dark-background-color
    );
  }
}

/// Note: This was only added because there was an issue with stylelint using @include as the only child within a keyframes
///
/// @param {String} name - the keyframes animation name.
/// @content The keyframes content.
/// @access private
@mixin keyframes($name) {
  @keyframes #{$name} {
    @content;
  }
}

/// This should only be used if not using the `transition-styles` mixin.
///
/// @see {mixin} transition-styles
@mixin collapse-transition {
  .rmd-collapse {
    transition-property: max-height, padding-bottom, padding-top;
    will-change: max-height, padding-bottom, padding-top;

    &--no-overflow {
      overflow: hidden;
    }

    &--enter {
      transition-timing-function: $deceleration-timing-function;
    }

    &--leave {
      transition-timing-function: $acceleration-timing-function;
    }
  }
}

/// This should only be used if not using the `transition-styles` mixin.
///
/// @see {mixin} transition-styles
@mixin cross-fade-transition {
  .rmd-cross-fade {
    opacity: 0;
    transform: translateY($cross-fade-translate-distance);

    &--active {
      opacity: 1;
      transform: translateY(0);
      transition-duration: $cross-fade-transition-duration;
      transition-property: opacity, transform;
      transition-timing-function: $deceleration-timing-function;
    }
  }
}

/// This should not be used if the `transition-styles` mixin is used.
///
/// @see {mixin} transition-styles
@mixin scale-transition {
  .rmd-scale-transition--enter {
    opacity: 0;
    transform: scale(0);
  }

  .rmd-scale-transition--enter-active {
    opacity: 1;
    transform: scale(1);
    transition-duration: $scale-transition-enter-duration;
    transition-property: opacity, transform;
    transition-timing-function: $deceleration-timing-function;
  }

  .rmd-scale-transition--exit {
    opacity: 1;
    transform: scale(1);
  }

  .rmd-scale-transition--exit-active {
    opacity: 0;
    transform: scale(0);
    transition-duration: $scale-transition-leave-duration;
    transition-property: opacity, transform;
    transition-timing-function: $acceleration-timing-function;
  }
}

/// This should not be used if the `transition-styles` mixin is used.
///
/// @see {mixin} transition-styles
@mixin scale-y-transition {
  .rmd-scale-transition--y-enter {
    transform: scaleY(0);
    transform-origin: 0 0;
  }

  .rmd-scale-transition--y-enter-active {
    transform: scaleY(1);
    @if $scale-y-transition-enter-duration != $scale-transition-enter-duration {
      transition-duration: $scale-y-transition-enter-duration;
    }
  }

  .rmd-scale-transition--y-exit {
    transform: scaleY(1);
    transform-origin: 0 0;
  }

  .rmd-scale-transition--y-exit-active {
    transform: scaleY(0);
    @if $scale-y-transition-leave-duration != $scale-transition-leave-duration {
      transition-duration: $scale-y-transition-leave-duration;
    }
  }
}

/// This should not be used if the `transition-styles` mixin is used.
///
/// @see {mixin} transition-styles
@mixin skeleton-placeholder-animation {
  .rmd-skeleton-placeholder {
    @include transition-use-var(
      background-color,
      skeleton-placeholder-background-color
    );
    @include transition-use-var(height, skeleton-placeholder-height);

    animation: rmd-skeleton-placeholder $skeleton-placeholder-animation-duration
      $skeleton-placeholder-animation-timing-function infinite;
    border-radius: $skeleton-placeholder-border-radius;
    color: transparent;

    // this was added so that at least _something_ renders on SSR. It would be
    // nice to have SSR-safe random numbers for this.
    width: $skeleton-placeholder-width;
  }

  @include keyframes(rmd-skeleton-placeholder) {
    @include utils.map-to-animation($skeleton-placeholder-animation);
  }
}

/// This should not be used if the `transition-styles` mixin is used.
///
/// @see {mixin} transition-styles
@mixin slide-transition {
  .rmd-slide-container {
    --p1-start-x: var(--p1-start);
    --p1-start-y: 0;
    --p1-end-x: var(--p1-end);
    --p1-end-y: 0;
    --p2-start-x: var(--p2-start);
    --p2-start-y: 0;
    --p2-end-x: var(--p2-end);
    --p2-end-y: 0;

    align-items: flex-start;
    display: flex;
    flex-wrap: nowrap;
    height: 100%;
    overflow: hidden auto;
    width: 100%;

    &--forward {
      --p1-start: 0;
      --p2-start: 0;
      --p1-end: -100%;
      --p2-end: -100%;
    }

    &--backward {
      --p1-start: -100%;
      --p2-start: -100%;
      --p1-end: 0;
      --p2-end: 0;
    }

    &--vertical {
      --p1-start-x: 0;
      --p1-start-y: var(--p1-start);
      --p1-end-x: 0;
      --p1-end-y: var(--p1-end);
      --p2-start-x: 0;
      --p2-start-y: var(--p2-start);
      --p2-end-x: 0;
      --p2-end-y: var(--p2-end);
    }
  }

  .rmd-slide {
    flex: 1 0 auto;
    height: 100%;
    width: 100%;
    will-change: transform;

    &--animate {
      transition: transform transition-get-var(slide-duration)
        $slide-timing-function;
    }

    &--enter {
      transform: translate3d(var(--p1-start-x), var(--p1-start-y), 0);
    }

    &--enter-active {
      transform: translate3d(var(--p1-end-x), var(--p1-end-y), 0);
    }

    &--exit {
      transform: translate3d(var(--p2-start-x), var(--p2-start-y), 0);
    }

    &--exit-active {
      transform: translate3d(var(--p2-end-x), var(--p2-end-y), 0);
    }
  }
}

/// Conditionally applies the css variables based on feature flags
@mixin transition-variables {
  @if not $disable-skeleton-placeholder {
    @include transition-set-var(
      skeleton-placeholder-background-color,
      $skeleton-placeholder-background-color
    );
    @include transition-set-var(
      skeleton-placeholder-height,
      $skeleton-placeholder-height
    );
  }

  @if not $disable-slide-transition {
    @include transition-set-var(slide-duration, $slide-duration);
  }

  @if not $disable-max-width-transition {
    @include transition-set-var(max-width, $max-width-transition-default);

    @if not $disable-max-width-transition-gap {
      @include transition-set-var(max-width-gap, 0);
    }
  }
}

/// Generates all the styles based on feature flags.
///
/// @param {Boolean} disable-layer [false] - Set this to `true` to disable the
/// layer behavior
@mixin transition-styles($disable-layer: false) {
  @include utils.optional-layer(
    transition,
    $disable-layer or $disable-transitions
  ) {
    @if not $disable-collapse-transition {
      @include collapse-transition;
    }

    @if not $disable-cross-fade-transition {
      @include cross-fade-transition;
    }

    @if not $disable-scale-transition or not $disable-scale-y-transition {
      @include scale-transition;
    }

    @if not $disable-scale-y-transition {
      @include scale-y-transition;
    }

    @if not $disable-skeleton-placeholder {
      @include skeleton-placeholder-animation;
    }

    @if not $disable-slide-transition {
      @include slide-transition;
    }

    @if not $disable-max-width-transition {
      .rmd-max-width-transition {
        @if not $disable-max-width-transition-gap {
          margin-right: utils.negate-var(transition-get-var(max-width-gap));
        }

        max-width: 0;
        overflow: hidden;
        transition: max-width
          $max-width-transition-duration
          $max-width-transition-timing-function;
        will-change: max-width;

        &--visible {
          @include transition-use-var(max-width);

          @if not $disable-max-width-transition-gap {
            margin-right: 0;
          }
        }

        @if not $disable-max-width-transition-gap {
          @include utils.rtl {
            margin-left: utils.negate-var(transition-get-var(max-width-gap));
            margin-right: 0;

            &--visible {
              margin-left: 0;
            }
          }
        }
      }
    }
  }
}
